using System;
using System.Collections;
//using IBatisNet.DataMapper;
using log4net;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Engine;
using Sedna.Core.Persistence.Hql;

namespace Sedna.Core.Persistence
{
	public class HqlExecutor : IHqlExecutor
	{

		private ILog log = LogManager.GetLogger(typeof(HqlExecutor));

		private IFilterCriteriaBuilder criteriaBuilder;
	    
		public ISession Session
		{
		    get
		    {
		        return NHibernateHelper.GetCurrentSession();
            }
		}

		public IFilterCriteriaBuilder CriteriaBuilder
		{
			set { criteriaBuilder = value; }
		}

		public ICollection Execute(string hqlQuery)
		{
			return Session.Find(hqlQuery);
		}

		public ICollection Execute(string hqlQuery, int start, int amount)
		{
            IQuery query = Session.CreateQuery(hqlQuery);
            query.SetFirstResult(start);
            query.SetMaxResults(amount);
            return query.List();
		}

		public IQueryResult Execute(IFilterState filterState)
		{
                ICriteriaFilterState criteriaFilterState = filterState as ICriteriaFilterState;
                if (criteriaFilterState != null)
                {
                    return Execute(criteriaFilterState);
                }
                IHqlFilterState hqlFilterState = filterState as IHqlFilterState;
                if (hqlFilterState != null)
                {
                    return Execute(hqlFilterState);
                }
                IHqlQueryFilterState hqlQueryFilterState = filterState as IHqlQueryFilterState;
                if (hqlQueryFilterState!=null)
                {
                    return Execute(hqlQueryFilterState);
                }
                IExternalCriteriaFilterState externalCriteriaFilterState = filterState as IExternalCriteriaFilterState; ;
                if (externalCriteriaFilterState != null)
                {
                    return Execute(externalCriteriaFilterState);
                }
                ISqlMapFilterState sqlMapFilterState = filterState as ISqlMapFilterState;
                if (sqlMapFilterState != null)
                {
                    return Execute(sqlMapFilterState);
                }
			throw new NotSupportedException("IFilterState must be either criteria or HQL based");
		}

	    public long GetTotalAmount(IFilterState filterState)
	    {
            try
            {
                ICriteriaFilterState criteriaFilterState = filterState as ICriteriaFilterState;
                if (criteriaFilterState != null)
                {
                    return GetTotalAmount(criteriaFilterState);
                }
                IHqlFilterState hqlFilterState = filterState as IHqlFilterState;
                if (hqlFilterState != null)
                {
                    return GetTotalAmount(hqlFilterState);
                }
                IHqlQueryFilterState hqlQueryFilterState = filterState as IHqlQueryFilterState;
                if (hqlQueryFilterState != null)
                {
                    return GetTotalAmount(hqlQueryFilterState);
                }
                IExternalCriteriaFilterState externalCriteriaFilterState = filterState as IExternalCriteriaFilterState; ;
                if (externalCriteriaFilterState != null)
                {
                    return GetTotalAmount(externalCriteriaFilterState);
                }
            }
            catch (Exception e)
            {
                log.Error(e.Message, e);
                throw e;
            }
            return -1;
        }

        #region Execute Helpers
        
	    private IQueryResult Execute(IHqlQueryFilterState filterState)
	    {
            IQuery query = filterState.ToHqlQuery(Session);
            query.SetFirstResult(filterState.StartIndex);
            query.SetMaxResults(filterState.Amount);
	        return new QueryResult(query.List());
	    }
	    
		private IQueryResult Execute(IExternalCriteriaFilterState filterState)
		{
			ICriteria criteria = criteriaBuilder.Build(filterState, filterState.PersistentType, Session);
			IList result = criteria.List();
			criteria.SetFirstResult(0);
			criteria.SetMaxResults(RowSelection.NoValue);
            return new QueryResult(result, Convert.ToInt32(GetICriteriaCount(criteria)));
		}

		private IQueryResult Execute(ICriteriaFilterState filterState)
		{
			ICriteria criteria = filterState.ToHqlCriteria(Session);
            criteria.SetFirstResult(filterState.StartIndex);
            criteria.SetMaxResults(filterState.Amount);
            return new QueryResult(criteria.List());
		}

		private IQueryResult Execute(IHqlFilterState filterState)
		{
            return new QueryResult(this.Execute(filterState.ToHqlString(), filterState.StartIndex, filterState.Amount));
        }

        #endregion

        #region GetTotalAmount Helpers

	    private long GetTotalAmount(ICriteriaFilterState filterState)
	    {
            ICriteria criteria = filterState.ToHqlCriteria(Session);
            return GetICriteriaCount(criteria);
	    }

        private long GetTotalAmount(IHqlQueryFilterState filterState)
        {
            IQuery query = filterState.ToHqlQuery(Session);
            long result = query.Count;
            log.Info(String.Format("Total count: {0}", result));
            return result;
        }

        private long GetTotalAmount(IHqlFilterState filterState)
        {
            IQuery query = Session.CreateQuery(filterState.ToHqlString());
            return query.Count;
        }

        private long GetTotalAmount(IExternalCriteriaFilterState filterState)
        {
            ICriteria criteria = criteriaBuilder.Build(filterState, filterState.PersistentType, Session);
            return GetICriteriaCount(criteria);
        }
	    
        private long GetICriteriaCount(ICriteria criteria)
        {
            criteria.SetProjection(Projections.RowCount());
            return (int)criteria.UniqueResult();	      
        }

	    #endregion

    }
}
